package cn.iocoder.yudao.module.system.tcpserver.protocol2;

import cn.hutool.core.util.HexUtil;
import cn.iocoder.yudao.module.system.dal.dataobject.zkdata.ZkDataDO;
import cn.iocoder.yudao.module.system.tcpserver.pojo.NewProtocolConstant;
import cn.iocoder.yudao.module.system.tcpserver.pojo.ProtocolConstant;
import cn.iocoder.yudao.module.system.tcpserver.utils.ZkCrcUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import java.util.Map;

public class NewZkProtocolDecoder extends LengthFieldBasedFrameDecoder {

    private static final int HEADER_SIZE = 10;

    /**
     * @param maxFrameLength      帧的最大长度
     * @param lengthFieldOffset   length字段偏移的地址
     * @param lengthFieldLength   length字段所占的字节长
     * @param lengthAdjustment    修改帧数据长度字段中定义的值，可以为负数 因为有时候我们习惯把头部记入长度,若为负数,则说明要推后多少个字段
     * @param initialBytesToStrip 解析时候跳过多少个长度
     * @param failFast            为true，当frame长度超过maxFrameLength时立即报TooLongFrameException异常，为false，读取完整个帧再报异
     */

    public NewZkProtocolDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {

        super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);

    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        //在这里调用父类的方法,实现指得到想要的部分,我在这里全部都要,也可以只要body部分
        in = (ByteBuf) super.decode(ctx, in);

        if (in == null) {
            return null;
        }
//        if (in.readableBytes() < HEADER_SIZE) {
//            throw new Exception("字节数不足");
//        }
        byte[] oriData = new byte[in.readableBytes()];
        in.getBytes(in.readerIndex(), oriData);

        int head = in.readUnsignedShort();
        if (head != ProtocolConstant.HEAD) {
            return null;
        }

        //读取length字段
        int length = in.readUnsignedShort();

        if (in.readableBytes() != length - 2 - 2) {
            throw new Exception("标记的长度不符合实际长度");
        }
        byte type1 = in.readByte();

        ZkDataDO result = new ZkDataDO();
        result.setOriData(HexUtil.encodeHexStr(oriData));

        //-------------------------------平台向海面设备请求上行-------------------------------
        if ((type1 & 0xFF) == NewProtocolConstant.HEART) {
            return processHeartBeat(result, in, length, oriData);
        }

        //请求上传指令响应
        if ((type1 & 0xFF) == NewProtocolConstant.UPLOAD_BEGIN) {
            return processUploadAck(ctx, result, in, length, oriData);
        }


        if (type1 == NewProtocolConstant.DEVICE01 || type1 == NewProtocolConstant.DEVICE02) {
            result.setDataSender(Integer.toHexString(type1));
            //数传 上行
            byte realDataType = in.readByte();
            if ((realDataType & 0xFF) == NewProtocolConstant.DATA) {
                //数传 包识别	Uint8	0xA1
                return processUploadDataBeginReq(result, in, length, oriData);

            }
            if ((realDataType & 0xFF) == NewProtocolConstant.DATA_END) {
                //传输结束 	包识别	Uint8	0xA4
                return processUploadEndData(result, in, length, oriData);

            }
            //数传下行 ***下行数据处理***
            if ((realDataType & 0xFF) == NewProtocolConstant.DATA_ACK) {
                int dataSeq = in.readUnsignedShort();
                if (dataSeq == 0xFFFF) {
                    result.setDataSeq(dataSeq);
                    //数据结束，结束！
                    return processDownLoadDataEndACK(result, in, length, oriData);
                } else if (dataSeq == 0) {
                    //数据开始的ack 发送第一条数据；
                    result.setDataSeq(dataSeq);
                    return processDownLoadDataACK(result, in, length, oriData);

                } else {
                    //继续发送下一包数据
                    result.setDataSeq(dataSeq);
                    return processDownLoadDataACK(result, in, length, oriData);
                }

            }
        }
//-------------------------------海面设备向平台请求下行-------------------------------
        //收到下行请求的响应
        if ((type1 & 0xFF) == NewProtocolConstant.DOWNLOAD_BEGIN) {
            String receiver = null;
            for (Map.Entry<String, NewHeartbeatServer.ChannelSchedule> stringChannelScheduleEntry : NewHeartbeatServer.getClientMap().entrySet()) {
                NewHeartbeatServer.ChannelSchedule value = stringChannelScheduleEntry.getValue();
                if (value.channel != null && value.channel == ctx.channel()) {
                    receiver = stringChannelScheduleEntry.getKey();
                    break;
                }
            }
            result.setDataSender(receiver);

            return processDownLoadAck(result, in, length, oriData);
        }

        //收到下行请求
//        if ((type1 & 0xFF) == NewProtocolConstant.DOWNLOAD_BEGIN) {
//            return processDownLoadReq(result, in, length, oriData);
//        }
        //收到数传ACK
        if (type1 == NewProtocolConstant.DEVICE01 || type1 == NewProtocolConstant.DEVICE02) {
            return processDownLoadReq(result, in, length, oriData);
        }
        return result;
    }

    //心跳 后续发送上行请求
    private ZkDataDO processHeartBeat(ZkDataDO result, ByteBuf in, int length, byte[] oriData) {
        result.setDataType((int) NewProtocolConstant.HEART);
        int receiver = in.readUnsignedShort();
        int sender = in.readUnsignedShort();
        processCrc(result, in, length, oriData);
        result.setDataReceiver(Integer.toHexString(receiver));
        result.setDataSender(Integer.toHexString(sender));
        return result;
    }

    // 平台发送上传指令-》收到指令回复 -》收到数据开始-》响应数据开始-》收到数据-》响应数据-》收到数据结束-》响应数据结束
    //收到指令回复 -处理请求上行响应 （平台向浮标发送上行请求后的浮标响应） 后续不处理
    private ZkDataDO processUploadAck(ChannelHandlerContext ctx, ZkDataDO result, ByteBuf in, int length, byte[] oriData) {
//        F4 01 AA
        byte type = in.readByte(); //01
        byte status = in.readByte();//AA
        if (type != NewProtocolConstant.ACK ||
                (status & 0xFF) != NewProtocolConstant.DATA_BEGIN) {

            result.setDataType(NewProtocolConstant.LOCAL_TYPE.ERROR);
        } else {
            result.setDataType(NewProtocolConstant.LOCAL_TYPE.UPLOAD_REQ_ACK);
        }
        int packageLength = in.readUnsignedShort();//本次数传数据包总量 todo
        result.setPacketLength(packageLength);
        if (packageLength == 0) {
            String receiver = null;
            for (Map.Entry<String, NewHeartbeatServer.ChannelSchedule> stringChannelScheduleEntry : NewHeartbeatServer.getClientMap().entrySet()) {
                NewHeartbeatServer.ChannelSchedule value = stringChannelScheduleEntry.getValue();
                if (value.channel != null && value.channel == ctx.channel()) {
                    receiver = stringChannelScheduleEntry.getKey();
                    break;
                }
            }
            result.setDataSender(receiver);
        }

        processCrc(result, in, length, oriData);
        return result;
    }


    //收到上行数传开始（浮标准备开始上传数据） ->需要回复 ACK
    private ZkDataDO processUploadDataBeginReq(ZkDataDO result, ByteBuf in, int length, byte[] oriData) {
        int dataReq = in.readUnsignedShort();
        if (dataReq == 0) {
            //数传开始包
            result.setDataType(NewProtocolConstant.LOCAL_TYPE.UPLOAD_DATA_BEGIN);
            result.setDataSeq(dataReq);
            int packageLength = in.readUnsignedShort();
            result.setPacketLength(packageLength);//本次数传数据包总量 todo
            processCrc(result, in, length, oriData);
        } else {
//            数据包
            result.setPacketLength(length);
            result.setDataLength(length - 8 - 2);
            result.setDataType(NewProtocolConstant.LOCAL_TYPE.UPLOAD_DATA);
            result.setDataSeq(dataReq & 0xFF);
//            result.getDataSender();
            result.setStatus(0);
            byte[] bytes = new byte[length - 8 - 2];
            in.readBytes(bytes);
            result.setDataPayload(HexUtil.encodeHexStr(bytes));
            processCrc(result, in, length, oriData);
        }
        return result;
    }

    //收到上行数传结束，->需要回复 ACK
    private ZkDataDO processUploadEndData(ZkDataDO result, ByteBuf in, int length, byte[] oriData) {
        int dataReq = in.readUnsignedShort(); //0xFFFF
        int dataSended = in.readUnsignedShort(); //已传输的数据 todo
        result.setDataType(NewProtocolConstant.LOCAL_TYPE.UPLOAD_END);
        processCrc(result, in, length, oriData);
        return result;
    }

    //下行
    //平台收到下行请求指令->平台响应指令回复->平台发送数据开始->平台收到回复->发送数据->收到响应->发送结束->收到响应

    private ZkDataDO processDownLoadAck(ZkDataDO result, ByteBuf in, int length, byte[] oriData) {
        byte type = in.readByte();// 0x01
        if (type != NewProtocolConstant.ACK) {
            result.setDataType(NewProtocolConstant.LOCAL_TYPE.ERROR);
        } else {
            result.setDataType(NewProtocolConstant.LOCAL_TYPE.DOWNLOAD_REQ_ACK);
            processCrc(result, in, length, oriData);
        }
        return result;
    }

    //处理请求下行 （向平台发送下行请求） ->需要回复 下行ACK 及数值开始包
    private ZkDataDO processDownLoadReq(ZkDataDO result, ByteBuf in, int length, byte[] oriData) {
        byte type = in.readByte();// 0x03
        if (type != NewProtocolConstant.REQ) {
            result.setDataType(NewProtocolConstant.LOCAL_TYPE.ERROR);
        } else {
            result.setDataType(NewProtocolConstant.LOCAL_TYPE.DOWNLOAD_REQ);
            processCrc(result, in, length, oriData);
        }
        return result;
    }

    //收到数传 ACK 需要持续发送数据，没数据后需要发送数传结束
    private ZkDataDO processDownLoadDataACK(ZkDataDO result, ByteBuf in, int length, byte[] oriData) {

        result.setDataType(NewProtocolConstant.LOCAL_TYPE.DOWNLOAD_DATA);
        int packageLength = in.readUnsignedShort();
        result.setPacketLength(packageLength);//本次数传数据包总量 todo
        processCrc(result, in, length, oriData);
        return result;
    }

    //收到数传结束 ACK
    private ZkDataDO processDownLoadDataEndACK(ZkDataDO result, ByteBuf in, int length, byte[] oriData) {
        result.setDataType(NewProtocolConstant.LOCAL_TYPE.DOWNLOAD_END_ACK);
        int packageLength = in.readUnsignedShort();
        result.setPacketLength(packageLength);//本次数传数据包总量 todo
        processCrc(result, in, length, oriData);
        return result;
    }

    private void processCrc(ZkDataDO result, ByteBuf in, int length, byte[] oriData) {
//        byte[] bytes = new byte[length - HEADER_SIZE - 2];
//        in.readBytes(bytes);
        byte crc1 = in.readByte();
        byte crc2 = in.readByte();
        byte[] crc = ZkCrcUtils.getCrc(oriData);
        if (crc[0] != crc1 || crc[1] != crc2) {
            result.setStatus(2);//异常数据
        }
    }

}
